
Public Module performance
    Public Class performanceCounterCategories
        Public Const process As String = "process"
        Public Const processor As String = "Processor"
        Public Const memory As String = "Memory"
    End Class

    Public Class performanceCounterCounters
        Public Const processorTime As String = "% Processor Time"
        Public Const availableMBytes As String = "Available MBytes"
        Public Const availableBytes As String = "Available Bytes"
        Public Const workingSet As String = "Working Set"
        Public Const virtualBytes As String = "Virtual Bytes"
    End Class

    Public Class performanceCounterInstances
        Public Const total As String = "_Total"
    End Class

    Public Function performanceCheck() As Boolean
        Return envs.perftrace()
    End Function

    Public Sub setperformanceCheck()
        envs.set_perftrace()
    End Sub

    Public Sub perfCheckPoint(Optional ByRef checkpoint As String = Nothing, _
                              Optional ByRef msg As String = Nothing, _
                              Optional ByVal forceOutput As Boolean = False, _
                              Optional ByVal additionalJump As Int32 = 0)
        If forceOutput OrElse performanceCheck() Then
            Dim errmsg As String = Nothing
            If isemptystring(checkpoint) Then
                errmsg = "Performance Monitor Checkpoint"
            Else
                errmsg = checkpoint
            End If
            If Not isemptystring(msg) Then
                errmsg += ", " + msg + "."
            Else
                errmsg += "."
            End If
            additionalJump += 1
            raiseError(errmsg, errorHandle.errorType.performance, additionaljump:=additionalJump)
        End If
    End Sub

    Public Sub perfMonitorStart(Optional ByRef msg As String = Nothing,
                                Optional ByVal forceOutput As Boolean = False, _
                                Optional ByVal additionalJump As Int32 = 0)
        perfCheckPoint("Performance Monitor Start", msg, forceOutput, additionalJump + 1)
    End Sub

    Public Sub perfMonitorEnd(Optional ByRef msg As String = Nothing,
                              Optional ByVal forceOutput As Boolean = False, _
                              Optional ByVal additionalJump As Int32 = 0)
        perfCheckPoint("Performance Monitor End", msg, forceOutput, additionalJump + 1)
    End Sub

    Private Sub refresh_process_status()
        currentProcess().Refresh()
    End Sub

    Private lastUsageTicks As Int64 = 0
    Private lastTicks As Int64 = 0
    Public Function recentProcessorUsage() As Double
        refresh_process_status()
        Dim ticks As Int64 = 0
        Dim usage As Int64 = 0
        ticks = now_t.global.lowres_ticks()
        usage = currentProcess().TotalProcessorTime().Ticks()
        If lastTicks = 0 Then
            lastTicks = currentProcess().StartTime().Ticks()
        End If
        Dim rtn As Double = 0
        If ticks = lastTicks Then
            rtn = usage - lastUsageTicks
        Else
            rtn = (usage - lastUsageTicks) / (ticks - lastTicks)
        End If
        lastUsageTicks = usage
        lastTicks = ticks
        Return rtn
    End Function

    Public Function processorUsage() As Double
        refresh_process_status()
        Return currentProcess().TotalProcessorTime().Ticks() /
               (now_t.global.lowres_ticks() - currentProcess().StartTime().Ticks())
    End Function

    Public Function privateBytesUsage() As Int64
        refresh_process_status()
        Return currentProcess().PrivateMemorySize64()
    End Function

    Public Function virtualBytesUsage() As Int64
        refresh_process_status()
        Return currentProcess().VirtualMemorySize64()
    End Function

    Public Function worksetBytesUsage() As Int64
        refresh_process_status()
        Return currentProcess().WorkingSet64()
    End Function
End Module

Namespace timelimitOperation
    Public Module _timelimitOperation
        Private ReadOnly actionTimeSuggestionMs As Int32
        Private _enabled As Boolean = True

        Private Function actionTimeMs() As Int64
            Return Now.milliseconds() - ts.startMs
        End Function

        Private Function exceedActionTimeSuggestion() As Boolean
            Return actionTimeMs() >= actionTimeSuggestionMs
        End Function

        Private Class ts
            <ThreadStatic()> Public Shared startMs As Int64
        End Class

        Public Sub start()
            If enabled() Then
                ts.startMs = Now.milliseconds()
            End If
        End Sub

        Private Sub finishImpl(Of T)(ByVal i As T)
            If enabled() AndAlso exceedActionTimeSuggestion() Then
                raiseError(errorHandle.errorType.performance,
                           "action ", i, " used ", actionTimeMs(), "ms, ", "exceeded actionSuggestionTime")
            End If
        End Sub

        Public Sub finish(ByVal identity As String)
            finishImpl(identity)
        End Sub

        Public Sub finish(ByVal ParamArray p() As Object)
            finishImpl(New lazyStrCat(p))
        End Sub

        Public Sub finish(ByVal d As lazier(Of String))
            finishImpl(d)
        End Sub

        Public Function enabled() As Boolean
            Return _enabled
        End Function

        Public Sub disable()
            Console.WriteLine(patchupErrMsg("timelimitOperation disabled.", errorHandle.errorType.performance))
            _enabled = False
        End Sub

        Public Sub enable()
            Console.WriteLine(patchupErrMsg("timelimitOperation enabled.", errorHandle.errorType.performance))
            _enabled = True
        End Sub

        Sub New()
            If isdebugbuild() Then
                actionTimeSuggestionMs = 300
            Else
                actionTimeSuggestionMs = 200
            End If
            If performanceCheck() Then
                enable()
            Else
                disable()
            End If
        End Sub
    End Module
End Namespace
